package org.yan.kangaroo.auth.config.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.yan.kangaroo.auth.config.properties.IgnoredUrlProperties;
import org.yan.kangaroo.auth.config.security.filter.CaptchaValidateFilter;
import org.yan.kangaroo.auth.config.security.filter.EmailVerifyCodeAuthenticationFilter;
import org.yan.kangaroo.auth.config.security.filter.JwtAuthenticationFilter;
import org.yan.kangaroo.auth.config.security.handler.McpAuthenticationFailHandler;
import org.yan.kangaroo.auth.config.security.handler.McpAuthenticationSuccessHandler;
import org.yan.kangaroo.auth.config.security.handler.RestAccessDeniedHandler;
import org.yan.kangaroo.auth.config.security.provider.EmailVerifyCodeAuthenticationProvider;
import org.yan.kangaroo.auth.config.security.provider.UsernamePasswordAuthenticationProvider;

/**
 * @author wangx
 * @date 11/08/2019 22:48
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private IgnoredUrlProperties ignoredUrlProperties;

    @Autowired
    private McpAuthenticationSuccessHandler mcpAuthenticationSuccessHandler;

    @Autowired
    private McpAuthenticationFailHandler mcpAuthenticationFailHandler;

    @Autowired
    private RestAccessDeniedHandler accessDeniedHandler;

    @Autowired
    private CaptchaValidateFilter captchaValidateFilter;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private EmailVerifyCodeAuthenticationProvider emailVerifyCodeAuthenticationProvider;

    @Autowired
    private UsernamePasswordAuthenticationProvider usernamePasswordAuthenticationProvider;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        auth.authenticationProvider(emailVerifyCodeAuthenticationProvider);
        auth.authenticationProvider(usernamePasswordAuthenticationProvider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http
                .authorizeRequests();

        // 除配置文件忽略路径其它所有请求都需经过认证和授权
        for (String url : ignoredUrlProperties.getUrls()) {
            registry.antMatchers(url).permitAll();
        }
        registry.and().formLogin()
                .loginPage("/auth-login")
                .loginProcessingUrl("/login")
                .permitAll()
                .successHandler(mcpAuthenticationSuccessHandler)
                .failureHandler(mcpAuthenticationFailHandler)
                .and()
                // 允许网页iframe
                .headers().frameOptions().disable()
                .and()
                .logout()
                .permitAll()
                .and()
                .authorizeRequests()
                // 任何请求
                .anyRequest()
                // 需要身份认证
                .authenticated()
                .and()
                // 允许跨域
                .cors().and()
                // 关闭跨站请求防护
                .csrf().disable()
                // 前后端分离采用JWT 不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                // 自定义权限拒绝处理类
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler)
                .and()
                .addFilterBefore(captchaValidateFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(emailVerifyCodeAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .addFilter(new JwtAuthenticationFilter(authenticationManager(), ignoredUrlProperties, redisTemplate));

    }

    @Bean
    public EmailVerifyCodeAuthenticationFilter emailVerifyCodeAuthenticationFilter() {
        EmailVerifyCodeAuthenticationFilter emailVerifyCodeAuthenticationFilter = new EmailVerifyCodeAuthenticationFilter(redisTemplate);
        emailVerifyCodeAuthenticationFilter.setAuthenticationManager(authenticationManager);
        emailVerifyCodeAuthenticationFilter.setAuthenticationSuccessHandler(mcpAuthenticationSuccessHandler);
        emailVerifyCodeAuthenticationFilter.setAuthenticationFailureHandler(mcpAuthenticationFailHandler);
        return emailVerifyCodeAuthenticationFilter;
    }

}
